#include <curl/curl.h>
#include <microhttpd.h>
//#include <mariadb/mysql.h>
#include <mariadb/mysql.h>
#include <cjson/cJSON.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ccas/ccas.h"
#include "web.h"

/* 用于记录数据库登陆信息 */
static struct DatabaseKeygen dk;

//------------------------------------------
/* 用于存储接收到的数据 */
struct ResponseData {
	char *data;
	size_t size;
};

/* 将接收到的数据存入 ResponseData 结构体中 */
size_t write_response_data(void *contents, size_t size, size_t nmemb, void *userp) {
	size_t realsize = size * nmemb;
	struct ResponseData *mem = (struct ResponseData *) userp;

	char *ptr = realloc(mem->data, mem->size + realsize + 1);
	if (ptr == NULL) {
		fprintf(stderr, "内存申请失败。\n");
		return 0;
	}
	mem->data = ptr;
	memcpy(&(mem->data[mem->size]), contents, realsize);
	mem->size += realsize;
	mem->data[mem->size] = 0;

	return realsize;
}

//------------------------------------------
/* 发起API访问的函数，返回值表示请求是否成功， 1表示成功， 0表示失败 */
int make_api_request() {
	CURL *curl_handle = NULL;
	CURLcode result;
	struct ResponseData response_data;
	
	response_data.data = NULL;
	response_data.size = 0;

	// 初始化 libcurl 全局环境
	result = curl_global_init(CURL_GLOBAL_DEFAULT);
	if (result != CURLE_OK) {
		fprintf(stderr, "关于 libcurl 的全局初始化失败: %s\n", curl_easy_strerror(result));
		return 0;
	}

	curl_handle = curl_easy_init();
	if (curl_handle == NULL) {
		fprintf(stderr, "关于 CURL 句柄的初始化失败。\n");
		curl_global_cleanup();
		return 0;
	}

	// 设置请求的URL以及写入回调等相关选项
	result = curl_easy_setopt(curl_handle, CURLOPT_URL, "http://localhost/login?username=root&password=baigezi");
	if (result != CURLE_OK) {
		fprintf(stderr, "为 CURL 句柄设置 URL 选项失败: %s\n", curl_easy_strerror(result));
		curl_easy_cleanup(curl_handle);
		curl_global_cleanup();
		return 0;
	}

	result = curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_response_data);
	if (result != CURLE_OK) {
		fprintf(stderr, "为 CURL 句柄设置写函数选项失败： %s\n", curl_easy_strerror(result));
		curl_easy_cleanup(curl_handle);
		curl_global_cleanup();
		return 0;
	}

	result = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&response_data);
	if (result != CURLE_OK) {
		fprintf(stderr, "为 CURL 设置写数据选项失败： %s\n", curl_easy_strerror(result));
		curl_easy_cleanup(curl_handle);
		curl_global_cleanup();
		return 0;
	}

	// 执行请求
	result = curl_easy_perform(curl_handle);
	if (result != CURLE_OK) {
		fprintf(stderr, "API 请求失败: %s\n", curl_easy_strerror(result));
		curl_easy_cleanup(curl_handle);
		curl_global_cleanup();
		return 0;
	}

	// 如果请求成功，输出获取到的内容
	if (response_data.data != NULL) {
		// 可以在这里添加更多对获取到的数据的处理逻辑，比如保存到文件等
		printf("%s\n", response_data.data);
	} else {
		fprintf(stderr, "收到空数据。\n");
	}

	// 释放之前分配的内存
	if (response_data.data != NULL) {
		free(response_data.data);
	}
	// 清理 CURL 相关资源
	curl_easy_cleanup(curl_handle);
	curl_global_cleanup();

	return 1; // 请求成功，返回1
}

int main(int argc, char *const *argv) {

	if (argc == 1) goto get_help;

	for (int i = 0; i < argc; i++) {
		if (argv[i][0] == '-' && argv[i][1] != '-') {
			size_t len = strlen(argv[i]);
			for (int j = 1; j < len; j++) {
				switch(argv[i][j]) {

					case 'h': { /* --help */

get_help:
						printf(	"-h\t获取帮助。\n"
							"-n\t创建新配置文件（慎）\n"
							"-g\t以 get 方式请求数据。\n"
							"-s\t运行 http 服务器。\n");
						return 0;

					} case 'n': { /* --new */

						if (save_json_conf(CONF_PATH)) {
							fprintf(stderr, "新建配置文件失败。\n");
							return 1;
						} else { return 0; }

					} case 'g': { /* --get */

						int success = make_api_request();
						if (success) {
							;
						} else {
							fprintf(stderr, "请求失败。\n");
						}
						return !success;

					} case 's': { /* --server */

					//--------------------------------------------------------------------------

						if (read_json_conf(CONF_PATH, &dk)) {
							fprintf(stderr, "读取配置失败。\n");
							return 1;
						}

						// 初始化 MySQL 库
						if (mysql_library_init(0, NULL, NULL)) {
							fprintf(stderr, "不能初始化 MySQL 库。\n");
							return 1;
						}

						const int port = PORT;
						struct MHD_Daemon *daemon;

						daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port,
									  NULL, NULL, &answer_to_connection,
									  NULL, MHD_OPTION_END);
						if (daemon == NULL) {
							fprintf(stderr, "MHD_start_daemon() 失败。");
							mysql_library_end();
							release_json_conf(&dk);
							return 1;
						}


						printf("服务器运行在此端口 -> %d\n输入 q 按回车关闭。\n", port);
						while (getchar() != 'q');


						// 关闭守护进程
						MHD_stop_daemon(daemon);

						// 清理所有 MySQL 资源
						mysql_library_end();

						// 清理数据库登陆凭证
						release_json_conf(&dk);
						return 0;

					//--------------------------------------------------------------------------
					}
				}
			}
		}
	}

	return 0;
}

enum MHD_Result answer_to_connection(void *cls,
				struct MHD_Connection *connection,
				const char *url,
				const char *method,
				const char *version,
				const char *upload_data,
				unsigned long *upload_data_size,
				void **con_cls) {

//--------------------------------------------------

	// 如果是首次请求，初始化上下文。
	if (!*con_cls) {
		*con_cls = (void*) "首次调用";
		//return MHD_YES;
	}

//--------------------------------------------------
	struct Information info;
	InitInfo(&info);

	// 获取请求头的值
	// 这个函数的最后一个参数会传递到 迭代器回调函数
	MHD_get_connection_values(connection, MHD_HEADER_KIND, my_iterator, NULL);
	MHD_get_connection_values(connection, MHD_COOKIE_KIND, my_iterator, NULL);
	MHD_get_connection_values(connection, MHD_POSTDATA_KIND, my_iterator, NULL);
	MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, my_iterator, &info);
	MHD_get_connection_values(connection, MHD_FOOTER_KIND, my_iterator, NULL);

	printf("===================\n"
		"目标地址：%s\n"
	       "访问方式：%s\n"
	       "版本：%s\n"
	       "上传数据：%s\n"
	       "数据容量：%lu\n"
	       "------------------\n"
	       "用户名：%s\n"
	       "密码：%s\n"
	       "==================\n",
	       url, method, version, upload_data, *upload_data_size,
	       info.username, info.password);
	//--------------------------------------------

//--------------------------------------------------
	/* 静态文件 */
	if (!strcmp(url, "/global.css") && !strcmp(method, "GET")) {

		// 打开文件
		string s = ccas_rf(STATIC_GLOBAL_CSS);
		if (s==NULL) {
			fprintf(stderr, "打开文件失败：%s\n", STATIC_GLOBAL_CSS);
			return MHD_NO;
		}
		char *file = ccas_ws(s);
		struct MHD_Response *response;
		int ret;

		// 创建响应对象，使用 MHD_RESPMEM_COPY 表示我们提供的缓冲区需要被复制
		response = MHD_create_response_from_buffer(strlen(file), (void*)file, MHD_RESPMEM_MUST_COPY);

		// 设置响应头
		MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/css;charset=utf-8");

		// 将响应发送到客户端
		ret = MHD_queue_response(connection, MHD_HTTP_OK, response);

		// 销毁响应对象（注意：在 MHD_queue_response 之后调用是安全的，因为库会内部复制数据）
		MHD_destroy_response(response);
		

		free(file);
		ccas_free(&s);

		return (ret == MHD_YES) ? MHD_YES : MHD_NO;

	} else if (!strcmp(url, "/index.css") && !strcmp(method, "GET")) {

		// 打开文件
		string s = ccas_rf(STATIC_INDEX_CSS);
		if (s==NULL) {
			fprintf(stderr, "打开文件失败：%s\n", STATIC_INDEX_CSS);
			return MHD_NO;
		}
		char *file = ccas_ws(s);
		struct MHD_Response *response;
		int ret;

		// 创建响应对象，使用 MHD_RESPMEM_COPY 表示我们提供的缓冲区需要被复制
		response = MHD_create_response_from_buffer(strlen(file), (void*)file, MHD_RESPMEM_MUST_COPY);

		// 设置响应头
		MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/css;charset=utf-8");

		// 将响应发送到客户端
		ret = MHD_queue_response(connection, MHD_HTTP_OK, response);

		// 销毁响应对象（注意：在 MHD_queue_response 之后调用是安全的，因为库会内部复制数据）
		MHD_destroy_response(response);
		

		free(file);
		ccas_free(&s);

		return (ret == MHD_YES) ? MHD_YES : MHD_NO;

	} else if (!strcmp(url, "/index.js") && !strcmp(method, "GET")) {

		// 打开文件
		string s = ccas_rf(STATIC_INDEX_JS);
		if (s==NULL) {
			fprintf(stderr, "打开文件失败：%s\n", STATIC_INDEX_JS);
			return MHD_NO;
		}
		char *file = ccas_ws(s);
		struct MHD_Response *response;
		int ret;

		// 创建响应对象，使用 MHD_RESPMEM_COPY 表示我们提供的缓冲区需要被复制
		response = MHD_create_response_from_buffer(strlen(file), (void*)file, MHD_RESPMEM_MUST_COPY);

		// 设置响应头
		MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/javascript;charset=utf-8");

		// 将响应发送到客户端
		ret = MHD_queue_response(connection, MHD_HTTP_OK, response);

		// 销毁响应对象（注意：在 MHD_queue_response 之后调用是安全的，因为库会内部复制数据）
		MHD_destroy_response(response);
		

		free(file);
		ccas_free(&s);

		return (ret == MHD_YES) ? MHD_YES : MHD_NO;

	} else if (!strcmp(url, "/index.html") && !strcmp(method, "GET")) {

		// 打开文件
		string s = ccas_rf(STATIC_INDEX_HTML);
		if (s==NULL) {
			fprintf(stderr, "打开文件失败：%s\n", STATIC_INDEX_HTML);
			return MHD_NO;
		}
		char *file = ccas_ws(s);
		struct MHD_Response *response;
		int ret;

		// 创建响应对象，使用 MHD_RESPMEM_COPY 表示我们提供的缓冲区需要被复制
		response = MHD_create_response_from_buffer(strlen(file), (void*)file, MHD_RESPMEM_MUST_COPY);

		// 设置响应头
		MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/html;charset=utf-8");

		// 将响应发送到客户端
		ret = MHD_queue_response(connection, MHD_HTTP_OK, response);

		// 销毁响应对象（注意：在 MHD_queue_response 之后调用是安全的，因为库会内部复制数据）
		MHD_destroy_response(response);
		

		free(file);
		ccas_free(&s);

		return (ret == MHD_YES) ? MHD_YES : MHD_NO;
/*
	} else if (!strcmp(url, "/login.css") && !strcmp(method, "GET")) {

		// 打开文件
		string s = ccas_rf(STATIC_LOGIN_CSS);
		if (s==NULL) {
			fprintf(stderr, "打开文件失败：%s\n", STATIC_LOGIN_CSS);
			return MHD_NO;
		}
		char *file = ccas_ws(s);
		struct MHD_Response *response;
		int ret;

		// 创建响应对象，使用 MHD_RESPMEM_COPY 表示我们提供的缓冲区需要被复制
		response = MHD_create_response_from_buffer(strlen(file), (void*)file, MHD_RESPMEM_MUST_COPY);

		// 设置响应头
		MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/css;charset=utf-8");

		// 将响应发送到客户端
		ret = MHD_queue_response(connection, MHD_HTTP_OK, response);

		// 销毁响应对象（注意：在 MHD_queue_response 之后调用是安全的，因为库会内部复制数据）
		MHD_destroy_response(response);
		

		free(file);
		ccas_free(&s);

		return (ret == MHD_YES) ? MHD_YES : MHD_NO;

	} else if (!strcmp(url, "/login.js") && !strcmp(method, "GET")) {

		// 打开文件
		string s = ccas_rf(STATIC_LOGIN_JS);
		if (s==NULL) {
			fprintf(stderr, "打开文件失败：%s\n", STATIC_LOGIN_JS);
			return MHD_NO;
		}
		char *file = ccas_ws(s);
		struct MHD_Response *response;
		int ret;

		// 创建响应对象，使用 MHD_RESPMEM_COPY 表示我们提供的缓冲区需要被复制
		response = MHD_create_response_from_buffer(strlen(file), (void*)file, MHD_RESPMEM_MUST_COPY);

		// 设置响应头
		MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/javascript;charset=utf-8");

		// 将响应发送到客户端
		ret = MHD_queue_response(connection, MHD_HTTP_OK, response);

		// 销毁响应对象（注意：在 MHD_queue_response 之后调用是安全的，因为库会内部复制数据）
		MHD_destroy_response(response);
		

		free(file);
		ccas_free(&s);

		return (ret == MHD_YES) ? MHD_YES : MHD_NO;

	} else if (!strcmp(url, "/login.html") && !strcmp(method, "GET")) {

		// 打开文件
		string s = ccas_rf(STATIC_LOGIN_HTML);
		if (s==NULL) {
			fprintf(stderr, "打开文件失败：%s\n", STATIC_LOGIN_HTML);
			return MHD_NO;
		}
		char *file = ccas_ws(s);
		struct MHD_Response *response;
		int ret;

		// 创建响应对象，使用 MHD_RESPMEM_COPY 表示我们提供的缓冲区需要被复制
		response = MHD_create_response_from_buffer(strlen(file), (void*)file, MHD_RESPMEM_MUST_COPY);

		// 设置响应头
		MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/html;charset=utf-8");

		// 将响应发送到客户端
		ret = MHD_queue_response(connection, MHD_HTTP_OK, response);

		// 销毁响应对象（注意：在 MHD_queue_response 之后调用是安全的，因为库会内部复制数据）
		MHD_destroy_response(response);
		

		free(file);
		ccas_free(&s);

		return (ret == MHD_YES) ? MHD_YES : MHD_NO;
*/
	} else if (!strcmp(url, "/logo.jpg") && !strcmp(method, "GET")) {

		int ret;
		struct MHD_Response *response;
		FILE *image_file;

		// 打开图像文件
		image_file = fopen(STATIC_LOGO_JPG, "rb");
		if (image_file == NULL) {
			fprintf(stderr, "打开图片失败：%s\n", STATIC_LOGO_JPG);
			return MHD_NO;
		}

		// 创建一个响应对象，通过回调函数从文件读取数据来填充响应内容
		response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 200,
			&ahc_read_from_file, image_file, &free_file);
		if (response == NULL) {
			fclose(image_file);
			return MHD_NO;
		}

		// 设置响应头，指定内容类型为 image/jpeg
		MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "image/jpeg");

		// 将响应加入连接队列，准备发送给客户端
		ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
		MHD_destroy_response(response);

		return (ret == MHD_YES) ? MHD_YES : MHD_NO;

	} else if (!strcmp(url, "/favicon.ico") && !strcmp(method, "GET")) {

		int ret;
		struct MHD_Response *response;
		FILE *image_file;

		// 打开图像文件
		image_file = fopen(STATIC_FAVICON_ICO, "rb");
		if (image_file == NULL) {
			fprintf(stderr, "打开图片失败：%s\n", STATIC_FAVICON_ICO);
			return MHD_NO;
		}

		// 创建一个响应对象，通过回调函数从文件读取数据来填充响应内容
		response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 200,
			&ahc_read_from_file, image_file, &free_file);
		if (response == NULL) {
			fclose(image_file);
			return MHD_NO;
		}

		// 设置响应头，指定内容类型为 image/jpeg
		MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "image/x-icon");

		// 将响应加入连接队列，准备发送给客户端
		ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
		MHD_destroy_response(response);

		return (ret == MHD_YES) ? MHD_YES : MHD_NO;

	//--------------------------------------------
	} else if (!strcmp(url, "/") && !strcmp(method, "GET")) {

		// 响应数据
		const char *redirect_url = "/index.html";
		struct MHD_Response *response;
		int ret;

		// 创建响应对象，使用 MHD_RESPMEM_COPY 表示我们提供的缓冲区需要被复制
		response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);

		// 设置 Location 头，指定重定向的目标URL
		MHD_add_response_header(response, MHD_HTTP_HEADER_LOCATION, redirect_url);

		// 将响应发送到客户端
		ret = MHD_queue_response(connection, MHD_HTTP_FOUND, response);

		// 销毁响应对象（注意：在 MHD_queue_response 之后调用是安全的，因为库会内部复制数据 ）
		MHD_destroy_response(response);

		return (ret == MHD_YES) ? MHD_YES : MHD_NO;
/*
	} else if (!strcmp(url, "/login") && !strcmp(method, "POST")) {
		//info.kind = Infokind_Login;

		char hash[LENGTH_OF_HASH] = {0};
		sha256(info.password, hash);

		//------------------------------
		int uid = Query_from_User(&dk, info.username, hash);
		char text[LENGTH_OF_UID] = {0};
                sprintf(text,"%d", uid);

		//------------------------------

		// 响应数据
		struct MHD_Response *response;
		int ret;

		// 创建响应对象，使用 MHD_RESPMEM_COPY 表示我们提供的缓冲区需要被复制
		response = MHD_create_response_from_buffer(strlen(text), (void*)text, MHD_RESPMEM_MUST_COPY);

		// 设置响应头为 application/json
		MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/html");

		// 将响应发送到客户端
		ret = MHD_queue_response(connection, MHD_HTTP_OK, response);

		// 销毁响应对象（注意：在 MHD_queue_response 之后调用是安全的，因为库会内部复制数据 ）
		MHD_destroy_response(response);

		return (ret == MHD_YES) ? MHD_YES : MHD_NO;

	} else if (!strcmp(url, "/regiest") && !strcmp(method, "POST")) {
		//info.kind = Infokind_Regiest;

		char hash[LENGTH_OF_HASH] = {0};
		sha256(info.password, hash);

		// 试图插入到数据库
		const char *text;
		if (Insert_to_User(&dk, info.username, hash)) {
			text = "失败";
		} else {
			text = "成功";
		}

		// 响应数据
		struct MHD_Response *response;
		int ret;

		// 创建响应对象，使用 MHD_RESPMEM_COPY 表示我们提供的缓冲区需要被复制
		response = MHD_create_response_from_buffer(strlen(text), (void*)text, MHD_RESPMEM_MUST_COPY);

		// 设置响应头为 application/json
		MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/html");

		// 将响应发送到客户端
		ret = MHD_queue_response(connection, MHD_HTTP_OK, response);

		// 销毁响应对象（注意：在 MHD_queue_response 之后调用是安全的，因为库会内部复制数据 ）
		MHD_destroy_response(response);

		return (ret == MHD_YES) ? MHD_YES : MHD_NO;
*/
	} else {
		return MHD_NO;
	}
}

enum MHD_Result my_iterator(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) {


	// 在这里处理每个键值对
	printf("键名：%s，值：%s\n", key, value);

	switch (kind) {
	case MHD_HEADER_KIND:
		// 返回非零以继续迭代， 返回0以停止。
		return MHD_YES;
	case MHD_COOKIE_KIND:
		return MHD_YES;
	case MHD_POSTDATA_KIND: {
		return MHD_YES;
	}
	case MHD_GET_ARGUMENT_KIND: {
		struct Information *info = (struct Information*) cls;
	        //if (info->kind == Infokind_Login) {
		  // 复制之前先检查长度，避免溢出
		  if (!strcmp(key, "username") && strlen(value) < sizeof(info->username)) {
			  strcpy(info->username, value);
		  } else if (!strcmp(key, "password") && strlen(value) < sizeof(info->password)) {
			  strcpy(info->password, value);
		  }
		//}
		return MHD_YES;
	}
	case MHD_FOOTER_KIND:
		return MHD_YES;
	default:
		fprintf(stderr, "Unknow kind.\n");
		return MHD_NO;
	}

}

ssize_t ahc_read_from_file(void *cls, uint64_t pos, char *buf, size_t max)
{
	FILE *file = cls;
	size_t bytes_read;

	// Seek to the specified position in the file
	if (fseek(file, pos, SEEK_SET) != 0) {
		return MHD_CONTENT_READER_END_WITH_ERROR;	// End of file or error
	}

	// Read data from the file
	bytes_read = fread(buf, 1, max, file);
	if(ferror(file)) {
		return MHD_CONTENT_READER_END_WITH_ERROR;	// Error reading file
	}
	return (bytes_read) ? bytes_read : MHD_CONTENT_READER_END_OF_STREAM;
}

void free_file(void *cls)
{
	FILE *f = cls;
	if (f != NULL) {
		fclose(f);
	}
}

